#!/usr/bin/env python
#
# Copyright 2022 Huaqin Technology. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#    * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
#    * Neither the name of Huaqin Technology nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""Does tws codestyle check on c files.

The goal of this script is to identify places in the code that "may" be in
non-compliance with BD1 coding style. It does not attempt to fix up these
problems -- the point is to educate. It does also not attempt to find all
problems, or to ensure that everything it does find is legitimately a
problem.
"""

import sys
import re


_USAGE = """
Synatax: xxx.py <file> [files]
"""

# The test files for this script.
_test_files = None
# _test_files = ['./test_code/ux_manager.c', './test_code/ux_manager.h']

# The valid extentions for filename
_valid_extentions = ['c', 'h']
_valid_extentions_re_pattern = r'[ch]'

# lines加不加前后空行，对每次计算rang有影响，使用宏定义消除影响
_LINE_LENS_RANGE_START = 0 # 给range函数使用，起始行
_LINE_LENS_RANGE_STOP = 0 # 给range函数结束使用，结束行
_LINE_LENS_TRUE = 0 # lines的真实行数
_LINE_LENS_WITH_MARKER = 0 # lines加上了前后MARKER的行数

# 每一行的最大长度
_MAX_LINE_LENS = 80

class _TWSLintState(object):
  """Maintains code checker states"""
  def __init__(self) -> None:
    self.error_count = 0
  
  def ResetErrorCounts(self):
    self.error_count = 0


_clint_state = _TWSLintState()


def PrintUsage(message):
  """Print a brief usage string and exits, optionally with an error message.

  Args:
    message: The optional error message.
  """
  print(_USAGE)
  print('The valid file extentions for file name is:')
  print(_valid_extentions)

  if message:
    sys.exit('\nFatal Error: ' + message)
  else:
    sys.exit(1)



def Error(filename, linenum, category, confidence, message):
  """Logs the error we have found

  Args:
    filename: The name of the file containing the error.
    linenum: The number of the line containing the error.
    category: A string used to describe the error.
    confidence: A number from 1-5 representing a confidence score
      for the error. 
      5: We are certain of the problem.
      1: It could be a legitimate construct.
  """
  print('%s:%s:  [%s] [%d]  %s' % (
          filename, linenum, category, confidence, message))


def CheckFileNotEmpty(lines):
  if lines.count <= 3:
    return False
  else:
    return True


def GetFileNameWithoutPath(filename):
  """去掉带有路径的filename，返回不带路径的name_wo_path

  Args:
    filename: The file name containing the lines
  """
  name = re.sub(r'\\', '/', filename)
  name = re.split(r'/', name)[-1]
  if not name:
    PrintUsage('filename error' + filename)

  return name


def CountLeftSpace(line):
  """计算字符串左侧的空格数量，返回空格数量

  Args:
    filename: The file name containing the lines
  """
  return len(line) - len(line.lstrip())


def GuardCheckForFilenameFormat(filename, file_extention, lines):
  """检查文件名是否全部为小写和下划线xxx_xxx.h，不能以下划线开头

  Args:
    filename: The file name containing the lines
    lines: The lines in the file.
  """
  _FILE_NAME_FORMAT_PATTERN = re.compile(r'^[a-z][a-z_]*.' + _valid_extentions_re_pattern + r'$')
  if not _FILE_NAME_FORMAT_PATTERN.match(GetFileNameWithoutPath(filename)):
    Error(filename, 0, 'whitespace/filename', 5,
      'Filename format error: "xxx_xxx.h"')


def GuardCheckForCopyright(filename, file_extention, lines):
  """检查文件0~10行是否有copyright

  Args:
    filename: The file name containing the lines
    lines: The lines in the file.
  """
  for line_num in range(_LINE_LENS_RANGE_START, min(_LINE_LENS_TRUE, 11)):
    if re.search(r'Copyright', lines[line_num], re.I):
      break

  Error(filename, 0, 'legal/copyright', 5,
        'No copyright message found.  '
        'You should have a line: "Copyright [year] <Copyright Owner>"')


def GuardCheckForMarcoFormat(filename, file_extention, lines):
  """检查'#if'等宏控是否有顶格书写.

  Args:
    filename: The file name containing the lines
    lines: The lines in the file.
  """
  rule = re.compile(r'\s+(#if|#endif|#elif|#define)')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if rule.match(lines[line_num]):
      Error(filename, line_num, 'whitespace/marco', 5,
        'Marco should with no indentation')


def GuardCheckForNewlineAtEndOfFile(filename, file_extention, lines):
  """检查文件最后是否有空行.

  Args:
    filename: The file name containing the lines
    lines: The lines in the file.
  """
  if lines[-2].strip():
    Error(filename, _LINE_LENS_TRUE, 'whitespace/ending_newline', 5,
      'Could not find a newline character at the end of the file.')


def GuardCheckForLineLength(filename, file_extention, lines):
  """检查行长度，不能超过_MAX_LINE_LENS定义的字符数量

  Args:
    lines:
  """
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if len(lines[line_num]) > _MAX_LINE_LENS:
      Error(filename, line_num, 'whitespace/line_length', 5,
        'Lines should be <= %i characters long, but you have %i' % (_MAX_LINE_LENS, len(lines[line_num])))


def GuardCheckForLineEndingWithBlank(filename, file_extention, lines):
  """检查行尾规范 TODO: 将行尾的空白字符去掉drop，便于后续规范检查，减少重复报错。
  1. 检查行尾是否有空白（包含空行、TAB、软换行）
  
  Args:
    lines:
  """
  _LINE_END_WITH_SPACE_PATTERN = re.compile(r'^.*\s+$')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _LINE_END_WITH_SPACE_PATTERN.match(lines[line_num]):
      Error(filename, line_num, 'whitespace/unused_space', 5,
        'Please remove unused space or tab at end of the line')


def GuardCheckForTabInLine(filename, file_extention, lines):
  """检查每行是否有tab
  
  Args:
    lines:
  """
  _TAB_SYMBLE_IN_LINE_PATTERN = re.compile(r'\r|\t|\f')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _TAB_SYMBLE_IN_LINE_PATTERN.search(lines[line_num]):
      Error(filename, line_num, 'whitespace/tab', 5,
        'Please use space in stead of Tab')


def GuardCheckForLanguage(filename, file_extention, lines):
  """检查是否有中文字符
  
  Args:
    lines:
  """
  _LANGUAGE_CHINESE_PATTERN = re.compile(r'[\u4e00-\u9fa5]')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
      if _LANGUAGE_CHINESE_PATTERN.search(lines[line_num]):
        Error(filename, line_num, 'whitespace/language', 5,
          'Please use English instead of chinese.')


def GuardCheckForIncludeFormat(filename, file_extention, lines):
  """检查#include格式.
  1. #include顶行写
  2. #include后只有一个空格
  3. 需要使用双引号，不使用尖括号
  4. 头文件不要带路径, 如必须带路径, 需备注lint-supperession: "#TWS-lint: include-format"
  
  Args:
    lines:
  """
  _INCLUDE_PATTERN = re.compile(r'^\s*#include')
  _INCLUDE_FORMAT_PATTERN = re.compile(r'^#include "[^..].*\.h"$')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _INCLUDE_PATTERN.match(lines[line_num]) and not _INCLUDE_FORMAT_PATTERN.match(lines[line_num]):
      Error(filename, line_num, 'whitespace/include', 5,
        '"#include" format error')


def GuardCheckForIncludeInHeaderFile(filename, file_extention, lines):
  """头文件中不应该#include头文件。

  Args:
    lines:
  """
  if file_extention != 'h':
    return
  _INCLUDE_PATTERN = re.compile(r'^\s*#include')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _INCLUDE_PATTERN.match(lines[line_num]):
      Error(filename, line_num, 'whitespace/headerfile_without_include', 5,
        'Header file(xxx.h) prohibit include other file.')


def GuardCheckForHeaderFileHaveMarcoProtect(filename, file_extention, lines):
  """头文件，应使用同文件名的宏保护起来，且全部大写：
    filename: xxx_xxx.h
    #ifndef __XXX_XXX_H__ // first line
    #define __XXX_XXX_H__ // second line
    ///code
    #endif // __XXX_XXX_H__ //last line

  Args:
    lines:
  """
  if file_extention != 'h':
    return
  marco = "__" + re.sub(r'\.', '_', GetFileNameWithoutPath(filename).upper()) + "__"
  if (lines[1] != '#ifndef ' + marco) and (lines[2] != '#define ' + marco) and (lines[-3] != '#endif // ' + marco):
    Error(filename, 0, 'whitespace/header_file', 5,
        'Header file(xxx.h) "#ifndef __FILE_NAME_H__" format error.')

def GuardCheckForSrcFileUseExtern(filename, file_extention, lines):
  """禁止在c文件中使用extern：

  Args:
    lines:
  """
  if file_extention != 'c':
    return

  _EXTERN_PATTERN = re.compile(r'^\s*extern')
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _EXTERN_PATTERN.match(lines[line_num]):
      Error(filename, 0, 'whitespace/c_file', 5,
        'Src file(xxx.c) forbid to use "extern"')


# Finly start
def GuardCheckForFileNameMarco(filename, file_extention, lines):
  """检查头文件是否使用文件名宏包含

  Args:
    lines:
  """
  pass


def GuardCheckForSingleLineCommentAndReplaceByBlank(filename, file_extention, lines):
  """检查注释规范, 并将这些注释替换为空（不影响行数）
  "//":
    a. "//"符号与注释间，应有且只有一个空格
    b. 当"//"位于代码行尾时,应与代码间至少有1个空格
    c. 当"//"位于新行时，应位于代码上方一行，无空行，
    d. 当"//"位于新行时，缩进应保持与代码一致（在缩进中检查）
    e. 禁止"//"后无内容
    f. 不能同时有多于两个的斜杠，如'///'

  Args:
    lines:
  """
  # rule1 = re.compile(r'^\s*//') # 匹配新行注释
  # rule2 = re.compile(r'^\s*//((\s{2,})|(\S))') # 匹配新行注释规则
  # rule3 = re.compile(r'(\S+//)|(//((\s{2,})|(\S)))') # 匹配代码行注释规则
  # for line_num in range(0, len(lines)):
  #   line = lines[line_num]
  #   if rule1.match(line): # 新行
  #     if rule2.match(line): # "//"符号后, 有两个以上空格，或者没有空格
  #       Error(filename, line_num + 1, 'whitespace/sigle_line_comment', 5,
  #         'Sigle line comment (new line) format error.')
  #     if line_num + 1 < len(lines): # 忽略最后一行
  #       code_line = lines[line_num + 1]
  #       if not code_line.strip(): # 注释行与代码行间有空行
  #         Error(filename, line_num + 1, 'whitespace/sigle_line_comment', 5,
  #           'Sigle line comment (new line) format error, should beyond code line')
  #       if (len(line) - len(line.lstrip())) != (len(code_line) - len(code_line.lstrip())): # 检查注释行与代码行缩进是否相等
  #         Error(filename, line_num + 1, 'whitespace/sigle_line_comment', 5,
  #           'Sigle line comment (new line) format error, should have same indentation with code line')
  #   elif rule3.search(line): # 代码行, "//"符号前或后没有空格，或符号后有多个空格, elif确保此处非新行
  #     Error(filename, line_num + 1, 'whitespace/sigle_line_comment', 5,
  #       'Sigle line comment format error.')

  _SINGLE_LINE_COMMENTS_NEWLINE_MODE_PATTERN = re.compile(r'^\s*///*')
  _SINGLE_LINE_COMMENTS_NEWLINE_MODE_RULE_PATTERN = re.compile(r'^\s*// \S.*$')
  _SINGLE_LINE_COMMENT_CODE_LINE_MODE_PATTERN = re.compile(r'^\s*[^ \t\r\n\f/].*///*')
  _SINGLE_LINE_COMMENT_CODE_LINE_MODE_RULE_PATTERN = re.compile(r'\S // \S.*$')

  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _SINGLE_LINE_COMMENTS_NEWLINE_MODE_PATTERN.match(lines[line_num]): # 检查是否单行注释的新行模式
      if not  _SINGLE_LINE_COMMENTS_NEWLINE_MODE_RULE_PATTERN.match(lines[line_num]): # 检查注释规范是否正确。
        Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
          '(Newline mode) Format error syntax: "// xxx".')
      if line_num == _LINE_LENS_TRUE: # 注释不应该为最后一行
        Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
          '(Newline mode) Should not be the last line.')
      if not lines[line_num + 1].strip(): # 注释的下一行，应该有代码
        Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
          '(Newline mode) Should not be blank in next line.')
      if CountLeftSpace(lines[line_num]) != CountLeftSpace(lines[line_num + 1]): # 检查缩进是否与下一行相等
        # 下一行是否为"}"，若是，则应有4个空格的缩进
        if not (re.match(r'^\s*}.*$', lines[line_num + 1]) and (CountLeftSpace(lines[line_num]) == CountLeftSpace(lines[line_num + 1]) + 4)):
          Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
            '(Newline mode) Indentation error, should be same with next line.')
      # replace this lines as blank
      lines[line_num] = ''

    if _SINGLE_LINE_COMMENT_CODE_LINE_MODE_PATTERN.match(lines[line_num]):
      if not _SINGLE_LINE_COMMENT_CODE_LINE_MODE_RULE_PATTERN.search(lines[line_num]):
        Error(filename, line_num, 'whitespace/sigle_line_comment', 5,
          '(Code-line mode) format error. syntax: "code // comments"')
      # replace this comments as blank
      # print(line_num, lines[line_num])
      lines[line_num] = re.sub(r'//.*$', '', lines[line_num])
      # print(line_num, lines[line_num])


def GuardCheckForMultiLineCommentAndReplaceByBlank(filename, file_extention, lines):
  """检查多行注释规范"/* */":
  1. 当使用多行模式时，第一行为"/**", 中间行应使用" * ", " */"位于最后一行，注意空格。
  2. 当使用单行模式时，格式为"/* 内容 */",且应位于行尾
  3. 注释应位于被注释代码正上方，不能有空行

  Args:
    lines:
  """
  # rule1 = re.compile(r'/\*') # 匹配多行注释开始
  # rule1_1 = re.compile(r'^\s*/\**$') # 匹配注释开始行格式,是否为新行, 是否使用/**为第一行，无注释内容,格式："/**"
  # rule2 = re.compile(r'\*/') # 匹配多行注释结束
  # rule2_1 = re.compile(r'^ +\*/$') # 匹配注释结束行格式，应为新行，"*/"前至少有一个空格，"*/"后无其他字符
  # rule3 = re.compile(r'/\*.*\*/') # 匹配单行模式 "/*任意字符*/"
  # rule3_1 = re.compile(r'^\s*/\* \S+ \*/$') # 匹配单行模格式，使用新行模式"  /* test */"
  # rule4 = re.compile(r'^\s* ((\*$)|(\* \S+))') # 匹配中间行规则: " *"或" * test"
  # start_line = None # 记录开始行
  # end_line = None # 记录结束行
  # ignore_end = False # 当开始行不符合规范时，忽略后面的格式检查
  # for line_num in range(0, len(lines)):
  #   line = lines[line_num]
  #   if rule3.search(line):
  #     if not rule3_1.match(line):
  #       Error(filename, line_num + 1, 'whitespace/multi_line_comment', 5,
  #         'Format error. "/* test */"')
  #     if line_num + 1 < len(lines):
  #       if not lines[line_num + 1].strip():
  #         Error(filename, line_num + 2, 'whitespace/multi_line_comment', 5,
  #           'Format error. the next line is None')
  #         start_line = None # 当下一行为空时，不继续检查缩进
  #         end_line = None
  #     if line_num + 1 == len(lines):
  #       Error(filename, line_num + 1, 'whitespace/multi_line_comment', 5,
  #           'Format error, should not be the last line')
  #       start_line = None # 当下一行为空时，不继续检查缩进
  #       end_line = None
  #   elif rule1.search(line): # 定位多行注释的开始
  #     start_line = line_num
  #     if not rule1_1.match(line): # 检查多行注释的第一行是否符合规范
  #       Error(filename, line_num + 1, 'whitespace/multi_line_comment', 5,
  #         'Format error, please use "/**" for starting')
  #       ignore_end = True
  #   elif rule2.search(line): # 定位多行注释的结束
  #     end_line = line_num
  #     if not rule2_1.match(line): # 检查注释结束行规则
  #       Error(filename, line_num + 1, 'whitespace/multi_line_comment', 5,
  #         'Format error, please use " */" for ending')
  #     if line_num + 1 < len(lines):
  #       if not lines[line_num + 1].strip(): # 检查注释结束的下一行是否为空。
  #         Error(filename, line_num + 2, 'whitespace/multi_line_comment', 5,
  #           'Format error. the next line is None')
  #         start_line = None # 当下一行为空时，不继续检查缩进
  #         end_line = None
  #     if line_num + 1 == len(lines):
  #       Error(filename, line_num + 1, 'whitespace/multi_line_comment', 5,
  #           'Format error, should not be the last line')
  #       start_line = None # 当下一行为空时，不继续检查缩进
  #       end_line = None

  #   if start_line and end_line: # 已找到一个完整的多行注释，开始检查中间行规则，以及结束行是否紧邻代码行。
  #     if end_line == start_line + 1:
  #       Error(filename, line_num + 1, 'whitespace/multi_line_comment', 5,
  #         'At least 3 lines are required')
  #     else:
  #       for mid_line_num in range(start_line, end_line + 1): # 检查多行注释的中间行，缩进是否符合规范
  #         space_count_curr = len(lines[mid_line_num]) - len(lines[mid_line_num].lstrip())
  #         if mid_line_num + 1 < len(lines):
  #           space_count_next = len(lines[mid_line_num + 1]) - len(lines[mid_line_num + 1].lstrip())
  #         if mid_line_num == start_line:
  #           if (space_count_curr + 1 != space_count_next):
  #             Error(filename, mid_line_num + 2, 'whitespace/multi_line_comment', 5,
  #               'Please check the indentation1')
  #         elif (mid_line_num == end_line):
  #           if space_count_curr != space_count_next + 1:
  #             Error(filename, mid_line_num + 2, 'whitespace/multi_line_comment', 5,
  #               'Please check the indentation2')
  #         else: # 中间注释行
  #           if not rule4.match(lines[mid_line_num]):
  #             Error(filename, mid_line_num + 1, 'whitespace/multi_line_comment', 5,
  #               'Format error: " * "')
  #           if space_count_curr != space_count_next:
  #             Error(filename, mid_line_num + 2, 'whitespace/multi_line_comment', 5,
  #               'Please check the indentation3')
  #     start_line = None # reset the count
  #     end_line = None # reset the count
  _MULTI_LINE_COMMENT_SINGLE_LINE_MODE_PATTERN = re.compile(r'^\s*/\*.*\*/\s*$') # "newline /* comments */"
  _MULTI_LINE_COMMENT_SINGLE_LINE_MODE_RULE_PATTERN = re.compile(r'^\s*/\* \S+ \*/$')
  _MULTI_LINE_COMMENT_CODE_LINE_MODE_PATTERN = re.compile(r'\S.*/\*.*\*/') # "code /* comments */"
  _MULTI_LINE_COMMENT_CODE_LINE_MODE_RULE_PATTERN = re.compile(r'\S /\* \S.*\S \*/$')
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_START_PATTERN = re.compile(r'/\*+') # "/**"
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_START_RULE_PATTERN = re.compile(r'^\s*/\**$')
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_MID_RULE_PATTERN = re.compile(r'^\s* \*(.*\S)?$')
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_STOP_PATTERN = re.compile(r'\*/') # " */"
  _MULTI_LINE_COMMENT_MULIT_LINE_MODE_STOP_RULE_PATTERN = re.compile(r'^\s* \*/$')

  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _MULTI_LINE_COMMENT_SINGLE_LINE_MODE_PATTERN.match(lines[line_num]): # 是否为单行模式
      if not _MULTI_LINE_COMMENT_SINGLE_LINE_MODE_RULE_PATTERN.match(lines[line_num]): # 单行模式是否规范
        # print(line_num, lines[line_num])
        Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Single-line mode) Format error syntax: "   /* xxx */".')
      if line_num == _LINE_LENS_TRUE: # 注释不应该为最后一行
        Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Single-line mode) Should not be the last line.')
      if not lines[line_num + 1].strip(): # 注释的下一行，应该有代码
        Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Single-line mode) Should not be blank in next line.')
      if CountLeftSpace(lines[line_num]) != CountLeftSpace(lines[line_num + 1]): # 检查缩进是否与下一行相等
        # 下一行是否为"}"，若是，则应有4个空格的缩进
        if not (re.match(r'^\s*}.*$', lines[line_num + 1]) and (CountLeftSpace(lines[line_num]) == CountLeftSpace(lines[line_num + 1]) + 4)):
          Error(filename, line_num, 'whitespace/multi_line_comment', 5,
            '(Single-line mode) Indentation error, should be same with next line.')
      lines[line_num] = ''

  # 把单行模式全部替代空格后，重新开始匹配，避免注释影响。
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
    if _MULTI_LINE_COMMENT_CODE_LINE_MODE_PATTERN.search(lines[line_num]): # 代码行的单行模式， 缩进随代码行本身，不检查此行缩进
      if not _MULTI_LINE_COMMENT_CODE_LINE_MODE_RULE_PATTERN.search(lines[line_num]): # 注释是否规范
        Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Code-line mode) Syntax error: "code /* comments */$"')
      lines[line_num] = re.sub(r'/\*.*?\*/', '', lines[line_num])
  
  start_line = None # 记录是否已找到一个多行注释的起始行。
  # 把单行模式、代码行模式，全部代空后，只剩下多行模式的注释，包括新行的多行模式、和跨代码行的多行模式。
  for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP): 
    if _MULTI_LINE_COMMENT_MULIT_LINE_MODE_START_PATTERN.search(lines[line_num]): # 检查是否为多行模式的起始行
      if not _MULTI_LINE_COMMENT_MULIT_LINE_MODE_START_RULE_PATTERN.match(lines[line_num]): # 检查规范
        Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Multi-line mode) Start-line syntax error: "newline /**$"')
      start_line = line_num # 标记已找到注释起始行，后面开始匹配中间行和结束行

      # 检查多行模式开始行的缩进，从上到下，本行与下一行比较
      if CountLeftSpace(lines[line_num]) + 1 != CountLeftSpace(lines[line_num + 1]):
        Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Multi-line mode) Start-line indentation error.')

       # 将注释替换成空白，注意"/*"前面可能有代码。
      lines[line_num] = re.sub(r'/\*.*$', '', lines[line_num])

      continue # 寻找下一行，不能在开始行匹配中间行和结束行规则

    # 已经找到了注释开始行，开始匹配中间行和结束行
    if start_line:
      if not _MULTI_LINE_COMMENT_MULIT_LINE_MODE_STOP_PATTERN.search(lines[line_num]): # 中间行（不是结束行）
        if not _MULTI_LINE_COMMENT_MULIT_LINE_MODE_MID_RULE_PATTERN.match(lines[line_num]): # 检查中间行规则
          Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Multi-line mode) Mid-line syntax error: " * comments$"')
  
        # 检查多行模式的中间行缩进，从上到下，本行与下一行比较
          if CountLeftSpace(lines[line_num]) != CountLeftSpace(lines[line_num + 1]):
            Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Multi-line mode) Mid-line indentation error.')

        lines[line_num] = '' # 直接将中间行替换为空白
      else: # 结束行
        if not _MULTI_LINE_COMMENT_MULIT_LINE_MODE_STOP_RULE_PATTERN.match(lines[line_num]): # 检查结束行规则
          Error(filename, line_num, 'whitespace/multi_line_comment', 5,
          '(Multi-line mode) Stop-line syntax error: " */$')

        """
        检查多行模式的结束行缩进，从上到下，本行与下一行比较
        若下一行不为空（有代码），则此多行注释应匹配代码行缩进
  
        当下一行为空，则此多行注释应该为顶行注释（所以如果此多行注释在{}，则注释末行的下一行，一定不能为空）
          当下一行为空时。由于之前已经将"//"和单行模式的"/**/"都替换为空了，所以此处下一行也有可能是被置换为空的那行（原先为单行注释）。
          所以多行模式，紧接下一行，如果为单行注释，这种情况是不允许的。
        """
        if not lines[line_num + 1].strip(): # 下一行不为空
          if CountLeftSpace(lines[line_num]) != CountLeftSpace(lines[line_num + 1]) + 1:
            Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Multi-line mode) Stop-line indentation error. Next line is not None.')
        else:
          if CountLeftSpace(lines[line_num]) != 1:
            Error(filename, line_num, 'whitespace/multi_line_comment', 5,
              '(Multi-line mode) Stop-line indentation error. Next line is None.')
            
        start_line = None # 注释已经结束，重置起始标志，便于下一个注释的查找。

        # 将注释替换为空白，注意'*/'后面可能还有代码，使用非贪婪模式
        lines[line_num] = re.sub(r'.*?\*/', '', lines[line_num])

      # print(line_num, lines[line_num])
  # print(lines)
      


def GuardCheckForVariableType(filename, file_extention, lines):
  """检查变量类型，是否在公共文件定义中
  
  Args:
    lines:
  """
  pass


def GuardCheckForFunctionComment(filename, file_extention, lines):
  """检查函数是否有注释
  1. ".c"文件的函数中应有注释
  2. 注释应符合规范
  
  Args:
    lines:
  """
  pass


def GuardCheckForIndent(filename, file_extention, lines):
  """检查缩进是否规范
  
  Args:
    lines:
  """
  pass


def GuardCheckForFunctionDefine(filename, file_extention, lines):
  """检查函数定义是否规范
  1. 中括号
  2. 圆括号规范
  2. 空格规范
  
  Args:
    lines:
  """
  pass


def GuardCheckForVariableDefine(filename, file_extention, lines):
  """检查变量命名是否规范
  1. 中括号
  2. 全局变量
  3. 局部变量
  4. 指针
  5. 空格
  
  Args:
    lines:
  """
  pass


def GuardCheckForStructDefine(filename, file_extention, lines):
  """检查结构体定义是否规范
  1. 中括号位置
  2. 空格

  Args:
    lines:
  """
  pass


def GuardCheckForEnumDefine(filename, file_extention, lines):
  """检查枚举类型是否规范
  1. 中括号
  2. 全局变量
  3. 局部变量
  4. 指针
  
  Args:
    lines:
  """
  pass


def GuardCheckOperaterFormat(filename, file_extention, lines):
  """运算符使用是否规范
  1. 运算符前后空格
  
  Args:
    lines:
  """
  pass


def GuardCheckForMalloc(filename, file_extention, lines):
  """提醒此处有内存分配,review时注意是否有释放内存
  
  Args:
    lines:
  """
  pass


def GuardCheckForArraySize(filename, file_extention, lines):
  """数组大小不应超过固定值，否则应检查
  
  Args:
    lines:
  """
  pass


def GuardCheckForFuntionSize(filename, file_extention, lines):
  """检查函数是否超长
  
  Args:
    lines:
  """
  pass


def GuardCheckForBrackesStandard(filename, file_extention, lines):
  """检查中括号的使用规范
  
  Args:
    lines:
  """
  pass



def GuardFindoutAllMultiLineBLock(filename, file_extention, lines):
  """找出所有多行定义块，并将行号存放于对应的list中
  1. 
  
  Args:
    lines:
  """
  pass



def ProcessFileLines(filename, file_extention, lines):
  GuardCheckForFilenameFormat(filename, file_extention, lines) # 检查文件命名规范
  GuardCheckForCopyright(filename, file_extention, lines) # 检查是否包含copyright
  GuardCheckForMarcoFormat(filename, file_extention, lines) # 检查 #if等宏是否顶行书写
  GuardCheckForNewlineAtEndOfFile(filename, file_extention, lines) # 检查文件最后一行应为空行
  GuardCheckForLineLength(filename, file_extention, lines) # 检查每一行的长度（包含空格）
  GuardCheckForLineEndingWithBlank(filename, file_extention, lines) # 检查行尾是否有多余空白
  GuardCheckForTabInLine(filename, file_extention, lines) # 检查文件中是否有tab符号
  GuardCheckForLanguage(filename, file_extention, lines) # 文件中不应包含中文字符
  GuardCheckForIncludeFormat(filename, file_extention, lines) # 检查#include格式
  GuardCheckForIncludeInHeaderFile(filename, file_extention, lines) # 头文件中不能有#include
  GuardCheckForHeaderFileHaveMarcoProtect(filename, file_extention, lines) # 头文件应使用#ifdef _FILE_NAME_H_包含
  GuardCheckForSrcFileUseExtern(filename, file_extention, lines) # .c文件中不应使用extern
  GuardCheckForSingleLineCommentAndReplaceByBlank(filename, file_extention, lines) # 检查"//"注释规范 must
  GuardCheckForMultiLineCommentAndReplaceByBlank(filename, file_extention, lines) # 检查"/**/"注释规范 must
  # for line_num in range(_LINE_LENS_RANGE_START, _LINE_LENS_RANGE_STOP):
  #   print(line_num, lines[line_num])
  # GuardFindoutAllMultiLineBLock(filename, file_extention, lines)

  pass


def ProcessFile(filename):
  """Does coding style check on a single file.
  
  Args:
    filename: The name of the file to check.
  """
  file_extention = filename[filename.rfind('.') + 1:]
  if file_extention not in  _valid_extentions:
    print('Unsupported file extention:' + '[' + filename +']')
    return

  try:
    lines = open(filename, mode='r', encoding='utf8').read().split("\n")
  except:
    print("Could not open file:" + filename)
    return

  if CheckFileNotEmpty is False:
    print('File is empty, no need check:' + filename)
    return

  lines = (['// marker so line numbers and indices both start at 1'] + lines +
           ['// marker so line numbers end in a known way'])
 
  global _LINE_LENS_RANGE_START
  global _LINE_LENS_RANGE_STOP
  global _LINE_LENS_TRUE
  global _LINE_LENS_WITH_MARKER

  _LINE_LENS_RANGE_START = 1
  _LINE_LENS_RANGE_STOP = len(lines) -1
  _LINE_LENS_TRUE = len(lines) - 2
  _LINE_LENS_WITH_MARKER = len(lines)

  ProcessFileLines(filename, file_extention, lines)


def main():
  filenames = sys.argv[1:]
  if _test_files:
    filenames = _test_files

  if not filenames:
    PrintUsage('No file specified')

  _clint_state.ResetErrorCounts()

  for filename in filenames:
      ProcessFile(filename)

  # Print the error counts
  sys.exit(_clint_state.error_count > 0)

if __name__ == '__main__':
    main()
